<template>
    <el-dialog title="日志详情" v-model="dialogVisible" :close-on-click-modal="false" destroy-on-close
        class="log-detail-dialog" top="5vh">
        <el-scrollbar height="calc(90vh - 98px)">
            <el-descriptions :column="1" border>
                <template v-for="(item, index) in descriptionItems" :key="index">
                    <el-descriptions-item v-if="!item.show || item.show(currentLog!)"
                        :label="item.label" :label-class-name="'label-fixed-width'">
                        <template v-if="item.type === 'tag'">
                            <el-tag :type="item.tagType?.(currentLog?.[item.prop]) || 'info'">
                                {{ item.format?.(currentLog?.[item.prop]) || currentLog?.[item.prop] }}
                            </el-tag>
                        </template>
                        <template v-else-if="item.type === 'pre'">
                            <pre>{{ formatJson(currentLog?.[item.prop]) }}</pre>
                        </template>
                        <template v-else>
                            {{ item.getValue?.(currentLog!) || currentLog?.[item.prop] }}
                        </template>
                    </el-descriptions-item>
                </template>
            </el-descriptions>
        </el-scrollbar>
    </el-dialog>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import {
    type LogDetailExtended,
    type DescriptionItem,
    type RequestMethod,
    type TagType,
    methodTagMap
} from '../types'

const props = defineProps<{
    visible: boolean
    currentLog: LogDetailExtended | null
}>()

const emit = defineEmits(['update:visible'])

const dialogVisible = computed({
    get: () => props.visible,
    set: (val) => emit('update:visible', val)
})

// 获取请求方法对应的标签类型
const getMethodTagType = (method?: string): TagType => {
    if (!method) return 'info'
    const upperMethod = method.toUpperCase() as RequestMethod
    return methodTagMap[upperMethod] ?? 'info'
}

// 格式化 JSON
const formatJson = (json: any): string => {
    try {
        return typeof json === 'string' ? JSON.stringify(JSON.parse(json), null, 2) : JSON.stringify(json, null, 2)
    } catch (e) {
        return String(json)
    }
}

// 格式化执行时间
const formatExecuteTime = (time?: number): string => {
    if (!time) return '0 ms'
    return `${time} ms`
}

// 获取执行时间对应的标签类型
const getExecuteTimeTagType = (time?: number): TagType => {
    if (!time) return 'info'
    if (time < 500) return 'success'
    if (time < 1000) return 'warning'
    return 'danger'
}

// 获取对象嵌套属性值
const getNestedValue = (obj: any, path: string): any => {
    return path.split('.').reduce((acc, part) => acc?.[part], obj)
}

// 描述项配置
const descriptionItems: DescriptionItem[] = [
    {
        label: '路由',
        prop: 'extend.module',
        getValue: (log) => `${getNestedValue(log, 'extend.module')}/${getNestedValue(log, 'extend.controller')}/${getNestedValue(log, 'extend.action')}`,
    },
    { label: '请求URL', prop: 'url' },
    {
        label: '请求方法',
        prop: 'method',
        type: 'tag',
        tagType: getMethodTagType,
    },
    { label: '请求头', prop: 'request_header', type: 'pre' },
    { label: '请求参数', prop: 'params', type: 'pre' },
    { label: '响应头', prop: 'response_header', type: 'pre' },
    { label: '响应结果', prop: 'result', type: 'pre' },
    { label: 'IP地址', prop: 'ip' },
    { label: '浏览器', prop: 'browser' },
    { label: '操作系统', prop: 'os' },
    {
        label: '状态码',
        prop: 'code',
        type: 'tag',
        tagType: (code: number) => code === 200 ? 'success' : 'danger',
    },
    { 
        label: '错误信息', 
        prop: 'error_msg', 
        type: 'pre', 
        show: (log) => !!log?.error_msg 
    },
    {
        label: '执行时间',
        prop: 'cost_time',
        type: 'tag',
        tagType: getExecuteTimeTagType,
        format: formatExecuteTime,
    },
    { label: '扩展信息', prop: 'extend', type: 'pre' },
    { label: '创建时间', prop: 'create_time' },
]
</script>

<style lang="scss" scoped>
:deep(.log-detail-dialog) {
    --el-dialog-margin-top: 10vh;

    .el-dialog__body {
        height: calc(80vh - 120px); // 减去头部和底部的高度
        padding: 0;
        overflow: hidden;
    }

    .el-scrollbar {
        height: 100%;
        padding: 20px;
    }
}

:deep(.el-descriptions__body) {
    background-color: var(--el-bg-color);
}

:deep(.label-fixed-width) {
    width: 120px !important;
    justify-content: flex-end;
    text-align: center !important;
}

pre {
    margin: 0;
    padding: 8px;
    background-color: var(--el-fill-color-light);
    border-radius: 4px;
    max-height: 300px;
    overflow: auto;
    font-family: monospace;
    white-space: pre-wrap;
    word-wrap: break-word;
}

:deep(.el-descriptions__cell) {
    .el-descriptions {
        margin: 0;
    }
}
</style>